#!/usr/bin/env bash

include "libs/test.sh"

declare arguments_include_cnt=0
function arguments()
{
    ((arguments_include_cnt++))
    eval "arguments_${arguments_include_cnt}_index=-1"
    eval "arguments_${arguments_include_cnt}_length=$#"
    eval "arguments_${arguments_include_cnt}_array=()"
    while [ "$#" != "0" ]; do
        eval "arguments_${arguments_include_cnt}_array+=(\"$1\")"
        shift
    done
    eval "arguments_${arguments_include_cnt}_skipped=()"
    eval "arguments_${arguments_include_cnt}_now=''"

    function arguments:is_option()
    {
        local now="$(arguments:get_now)"
        [ "${now:0:1}" == "-" ]
    }

    function arguments:is_kv_option()
    {
        egrep "\--\w*=\w*" <<< "$(arguments:get_now)" 1>/dev/null 2>&1
    }

    function arguments:get_now()
    {
        eval "echo \${arguments_${arguments_include_cnt}_now}"
    }

    function arguments:skip()
    {
        eval "arguments_${arguments_include_cnt}_skipped+=('$(arguments:get_now)')"
        arguments:next
    }

    function arguments:skip_all()
    {
        while arguments:have_next; do
            arguments:skip
        done
        return 0
    }

    function arguments:get_skipped()
    {
        eval "echo \${arguments_${arguments_include_cnt}_skipped[*]}"
    }

    function arguments:next()
    {
        if ! arguments:have_next; then
            echo "arguments: no more arg"
            exit 1
        fi
        eval "((arguments_${arguments_include_cnt}_index++))"
        local index_name="arguments_${arguments_include_cnt}_index"
        local index="${!index_name}"
        local array="arguments_${arguments_include_cnt}_array"
        eval "arguments_${arguments_include_cnt}_now=\"\${$array[$index]}\""
    }

    function arguments:parse_option()
    {
        local option="$(arguments:get_now)"
        while [ "${option:0:1}" == "-" ]; do
            option="${option:1}"
        done
        echo $option
    }

    function arguments:parse_key_of_kv_option()
    {
        local option="$(arguments:get_now)"
        option="${option:2}"
        echo ${option%%=*}
    }

    function arguments:parse_val_of_kv_option()
    {
        local option="$(arguments:get_now)"
        echo ${option#*=}
    }

    function arguments:have_next()
    {
        local index_name="arguments_${arguments_include_cnt}_index"
        local length_name="arguments_${arguments_include_cnt}_length"
        (( ${!index_name} < ${!length_name} ))
    }

    function arguments:destroy()
    {
        # delete variable
        unset "arguments_${arguments_include_cnt}_index"
        unset "arguments_${arguments_include_cnt}_array"
        unset "arguments_${arguments_include_cnt}_length"
        unset "arguments_${arguments_include_cnt}_skipped"
        unset "arguments_${arguments_include_cnt}_now"

        ((arguments_include_cnt--))

        # delete function
        if (( arguments_include_cnt == 0 )); then
            unset -f arguments:is_option
            unset -f arguments:is_kv_option
            unset -f arguments:get_now
            unset -f arguments:skip
            unset -f arguments:get_skipped
            unset -f arguments:next
            unset -f arguments:parse_key_of_kv_option
            unset -f arguments:parse_val_of_kv_option
            unset -f arguments:parse_option
            unset -f arguments:have_next
            unset -f arguments:destroy
        fi
    }

    arguments:next
}

function arguments:test1()
{
    arguments -c --output a --index 2 --success --filter="123" normal

    # -c
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:should_return_false arguments:is_kv_option
    test:run_cmd arguments:get_now
    test:should_be -c
    test:run_cmd arguments:parse_option
    test:should_be c
    arguments:next

    # --output a
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:should_return_false arguments:is_kv_option
    test:run_cmd arguments:get_now
    test:should_be --output
    test:run_cmd arguments:parse_option
    test:should_be output
    arguments:next

    test:should_return_true arguments:have_next
    test:should_return_false arguments:is_option
    test:run_cmd arguments:get_now
    test:should_be a
    test:run_cmd arguments:parse_option
    test:should_be a
    arguments:next

    # --index 2
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:should_return_false arguments:is_kv_option
    arguments:skip

    test:should_return_true arguments:have_next
    test:should_return_false arguments:is_option
    arguments:skip

    test:run_cmd arguments:get_skipped
    test:should_be "--index 2"
    skipped=($(arguments:get_skipped))
    test:run_cmd echo ${skipped[0]}
    test:should_be --index

    # --success
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:should_return_false arguments:is_kv_option
    test:run_cmd arguments:get_now
    test:should_be --success
    test:run_cmd arguments:parse_option
    test:should_be success
    arguments:next

    # --filter="123"
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:should_return_true arguments:is_kv_option
    test:run_cmd arguments:get_now
    test:should_be --filter="123"
    test:run_cmd arguments:parse_key_of_kv_option
    test:should_be filter
    test:run_cmd arguments:parse_val_of_kv_option
    test:should_be 123
    arguments:next

    # normal
    test:should_return_true arguments:have_next
    test:should_return_false arguments:is_option
    test:run_cmd arguments:get_now
    test:should_be normal
    arguments:next

    test:should_return_false arguments:have_next
    arguments:destroy
}
test:register_test arguments:test1

function arguments:test2()
{
    arguments -c
        arguments -a

        # -a
        test:should_return_true arguments:have_next
        test:should_return_true arguments:is_option
        test:run_cmd arguments:get_now
        test:should_be -a
        test:run_cmd arguments:parse_option
        test:should_be a
        arguments:next

        test:should_return_false arguments:have_next
        arguments:destroy

    # -c
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:run_cmd arguments:get_now
    test:should_be -c
    test:run_cmd arguments:parse_option
    test:should_be c
    arguments:next

    test:should_return_false arguments:have_next
    arguments:destroy
}
test:register_test arguments:test2

function arguments:test3()
{
    arguments - --

    # -
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:run_cmd arguments:get_now
    test:should_be -
    test:run_cmd arguments:parse_option
    test:should_be ""
    arguments:next

    # -
    test:should_return_true arguments:have_next
    test:should_return_true arguments:is_option
    test:run_cmd arguments:get_now
    test:should_be --
    test:run_cmd arguments:parse_option
    test:should_be ""
    arguments:next

    test:should_return_false arguments:have_next
    arguments:destroy
}
test:register_test arguments:test3
